<script setup>
import {computed, createVNode, defineExpose, defineProps, h, nextTick, onMounted, onUnmounted, ref, watch} from "vue";
import {sftpApi} from "@/api/sftp";
import {Button, message, Modal, notification} from "ant-design-vue";
import fileIcon from "@/assets/file-icon/dir.png";
import dirIcon from "@/assets/file-icon/file.png";
import _ from "lodash";
import {upload, uploadFile} from "@/utils/File";
import {ExclamationCircleOutlined} from "@ant-design/icons-vue";
import {computedFileSize} from "@/components/tinymce/File";
import {useAutoAnimate} from "@formkit/auto-animate/vue";
import {useAuthStore} from "@shared/store/useAuthStore";
import {uploadFileProcess} from "@/components/process.jsx";


let authStore = useAuthStore()

const props = defineProps({
  serverId: {
    type: [Number], default: undefined,
  },
  operationId: {
    type: [String], default: undefined,
  },
  serverName: {
    type: [String], default: '',
  },
  fixedPath: {
    type: [String], default: undefined,
  },
  path: {
    type: [String], default: '',
  }
});

let spinning = ref(false)
let spinTip = ref('')
const [parent, enable] = useAutoAnimate()

let currentPath = ref("/");
let currentPathEdit = ref("/")
let currentPathArray = computed(() => {
  let path = currentPath.value.split("/");
  path = path.filter(item => item !== "")
  let temp = "";

  let res = path.map(item => {
    temp += `/${item}`
    return {
      name: item,
      path: temp
    }
  })

  res.unshift({
    name: "/",
    path: "/"
  })

  return res
})
let currentFiles = ref([]);
let sessionId = ref("")

const init = async () => {
  spinning.value = true
  spinTip.value = "正在连接服务器"
  try {
    if (sessionId.value) {
      await sftpApi.close({id: sessionId.value})
    }
    sessionId.value = await sftpApi.init({serverId: props.serverId, sessionId: authStore.session})

    if (props.fixedPath) {
      currentPath.value = props.fixedPath
    } else {
      currentPath.value = await sftpApi.pwd({id: sessionId.value})
    }

  } catch (e) {
    console.error(e)
    message.error(e.message)
    return
  } finally {
    spinning.value = false
    spinTip.value = ""
  }

  await ls()
}

//按名字排序
const normalSort = (files) => {
  return _.sortBy(files, (file) => {
    return file.name
  })
}

const dateSort = (files) => {
  return _.orderBy(files, (file) => {
    return file.attributes.mtime
  }, ['desc'])
}

const typeSort = (files) => {
  return _.sortBy(files, (file) => {
    return file.attributes.type
  })
}

let sortFun = normalSort;

init()

const ls = async () => {
  spinning.value = true
  enable(false)
  searchValue.value = ''
  spinTip.value = "正在获取文件列表"
  page.value = 1
  try {
    currentFiles.value = sortFun(await sftpApi.ls({id: sessionId.value, remotePath: currentPath.value}))
    return true
  } catch (e) {
    message.error(e.message)
    console.error(e)
    return false
  } finally {
    spinning.value = false
    spinTip.value = ""
    nextTick(() => {
      enable(true)
    })
  }
}

const changeDir = async (path) => {
  if (path === currentPath.value) {
    return
  }

  if (path < props.fixedPath) {
    return
  }

  let originPath = currentPath.value
  currentPath.value = path
  let res = await ls()
  if (!res) {
    currentPath.value = originPath
    await ls()
  }
}

const handleUpload = () => {
  uploadFile(sftpApi.upload({id: sessionId.value, remotePath: currentPath.value}), async (res, fileName) => {
    await ls()
  }, {remotePath: currentPath.value});
}

const uploadFileToCurrentPath = async (file) => {
  return await upload(sftpApi.upload({
    id: sessionId.value,
    remotePath: currentPath.value
  }), file, {remotePath: currentPath.value})
}

defineExpose({
  changeDir,
  init,
  serverId: props.serverId,
  operationId: props.operationId,
  sessionId: sessionId,
})

const handleDownload = (file) => {
  window.open(sftpApi.download({id: sessionId.value, remotePath: currentPath.value + '/' + file.name}))
}


let renameInputs = ref([])
let currentRenameFileIndex = ref(null)
let newFileName = ref('')
let renameFile = ref('')

const handleRename = async (file, index) => {
  renameFile.value = file
  newFileName.value = file.name
  currentRenameFileIndex.value = index
  await nextTick(() => {
    renameInputs.value.forEach(item => {
      item.focus()
    })
  })
}

const confirmRename = _.throttle(async () => {
  if (renameFile.value.name === newFileName.value) {
    currentRenameFileIndex.value = null
    return
  }

  try {
    await sftpApi.rename({
      id: sessionId.value,
      remotePath: currentPath.value + '/' + renameFile.value.name,
      newRemotePath: currentPath.value + '/' + newFileName.value
    })
  } catch (e) {
    console.error(e)
    message.error(e.message)
  }

  await ls()

  currentRenameFileIndex.value = null
}, 1000, {
  leading: true,
  trailing: false,
  maxWait: 1000,
})


const handleDel = (file) => {
  let rmApi = sftpApi.rm
  if (file.directory) {
    rmApi = sftpApi.rmDir
  }

  let modal = Modal.confirm({
    title: '确定要删除吗?',
    icon: createVNode(ExclamationCircleOutlined),
    content: file.type === 'DIR' ? '你要删除的是一个文件夹，请小心行事！！!' : '',
    onOk() {
      return rmApi({id: sessionId.value, remotePath: currentPath.value + '/' + file.name})
          .then(() => {
            message.success("删除成功")
            ls()
          })
          .catch((e) => {
            console.error(e)
            message.error(e.message)
          }).finally(() => {
            modal.destroy()
          })
    },
    onCancel() {
    },
  });
}

let mkdirVisible = ref(false)
let mkdirName = ref('')
const handleMkdir = () => {
  mkdirVisible.value = true
  mkdirName.value = ''
}
const mkdir = () => {
  if (!mkdirName.value) {
    message.error("请输入目录名")
    return
  }
  sftpApi.mkdir({id: sessionId.value, remotePath: currentPath.value + '/' + mkdirName.value})
      .then(() => {
        message.success("创建成功")
        ls()
      })
      .catch((e) => {
        console.error(e)
        message.error(e.message)
      })
  mkdirVisible.value = false
}


const handleClickFile = async (file) => {
  if (file.attributes.type === 'REGULAR') {
    fileViewVisible.value = true
    fileView.value = file
    return
  }

  await changeDir(currentPath.value + (currentPath.value !== '/' ? '/' : '') + file.name)
}

watch(currentFiles, (files) => {
  searchFiles.value = files
})

let searchFiles = ref([])

let searchValue = ref('')

const search = _.debounce(async (value) => {
  if (!value.target.value) {
    searchFiles.value = currentFiles.value
    return
  }
  //过滤currentFiles
  searchFiles.value = currentFiles.value.filter(item => item.name.includes(value.target.value))
})

let fileViewVisible = ref(false)
let fileView = ref({})

//permissions转换为rwx-rwx-rwx格式
//permissions : ["USR_W", "USR_R", "OTH_R", "GRP_R"]
const permissionsToRwx = (permissions) => {
  let res = ''
  if (permissions.includes('USR_R')) {
    res += 'r'
  } else {
    res += '-'
  }
  if (permissions.includes('USR_W')) {
    res += 'w'
  } else {
    res += '-'
  }
  if (permissions.includes('USR_X')) {
    res += 'x'
  } else {
    res += '-'
  }
  if (permissions.includes('GRP_R')) {
    res += 'r'
  } else {
    res += '-'
  }
  if (permissions.includes('GRP_W')) {
    res += 'w'
  } else {
    res += '-'
  }
  if (permissions.includes('GRP_X')) {
    res += 'x'
  } else {
    res += '-'
  }
  if (permissions.includes('OTH_R')) {
    res += 'r'
  } else {
    res += '-'
  }
  if (permissions.includes('OTH_W')) {
    res += 'w'
  } else {
    res += '-'
  }
  if (permissions.includes('OTH_X')) {
    res += 'x'
  } else {
    res += '-'
  }

  //每三个字符加一个,，最后一个不加
  let temp = ''
  for (let i = 0; i < res.length; i++) {
    temp += res[i]
    if ((i + 1) % 3 === 0 && i !== res.length - 1) {
      temp += ','
    }
  }

  return temp
}

let sortTypeValue = ref('normal')

const handleChangeSort = (e) => {
  sortFun = normalSort
  if (e === 'date') {
    sortFun = dateSort
  } else if (e === 'type') {
    sortFun = typeSort
  } else if (e === 'size') {
    sortFun = (files) => {
      return _.orderBy(files, (file) => {
        return file.attributes.size
      }, ['desc'])
    }
  }

  currentFiles.value = sortFun(currentFiles.value)
}

//组件关闭时，关闭sftp连接
onUnmounted(async () => {
  if (sessionId.value) {
    await sftpApi.close({id: sessionId.value})
  }
})

let currentPageData = computed(() => {
  return searchFiles.value.slice((page.value - 1) * size.value, page.value * size.value)
})

let page = ref(1)
let size = ref(100)

let editPathVisible = ref(false)

const handleChangePath = () => {
  editPathVisible.value = false
  changeDir(currentPathEdit.value)
}

let editInput = ref(null)
const startEditPath = () => {
  if (props.fixedPath) {
    return
  }
  editPathVisible.value = true
  currentPathEdit.value = currentPath.value
  nextTick(() => {
    editInput.value.focus()
  })
}

let width = ref("16.6%")

let sftpRootEl = ref(null)

let buttonType = ref('big')

onMounted(() => {
  //监测sftpRootEl的大小变化
  const observer = new ResizeObserver(_.debounce(() => {
    width.value = `${100 / Math.floor(sftpRootEl.value.clientWidth / 100)}%`
    if (sftpRootEl.value.clientWidth < 720) {
      buttonType.value = 'small'
    } else if (sftpRootEl.value.clientWidth < 1000) {
      buttonType.value = 'normal'
    } else {
      buttonType.value = 'big'
    }
  },200))

  observer.observe(sftpRootEl.value)
})


const ondragover = (e) => {
  e.preventDefault()
}


const drop = async (e, sessionId, currentPath) => {
  if (!(e.dataTransfer.types?.some(t => t === 'Files') || e.dataTransfer.types.length === 0)) {
    return
  }

  const isFileTransfer = e.dataTransfer.types?.some(t => t === 'Files');

  if (isFileTransfer) {
    Promise.all([...e.dataTransfer.files].map(file => uploadFileToCurrentPath(file)))
        .then(() => {
          message.success("全部上传成功")
          ls()
        })
    return
  }

  let sourceData = JSON.parse(localStorage.getItem('dragData'));

  if (sourceData.sessionId === sessionId) {
    return
  }


  let key = sourceData.sessionId + "---" + sourceData.currentPath + "/" + sourceData.fileName + "---" + currentPath + "/" + sourceData.fileName + "---" + sessionId
  let sftp = new BroadcastChannel('sftp')

  let time = new Date().getTime()

  const handleProcessMessage = (e) => {
    let data = JSON.parse(e.data)
    let messageKey = data.sourceId + "---" + data.sourceFileName + "---" + data.targetFileName + "---" + data.targetId
    if (key !== messageKey) {
      return
    }

    //取消
    if (data.progress === -1) {
      notification.close(key)
      ls()
      return
    }

    let take = Math.floor((new Date().getTime() - time) / 1000);
    if (take === 0) {
      take = 1
    }

    if (data.progress === sourceData.fileSize) {
      notification.open({
        key: key,
        duration: 0,
        message: '文件传输',
        description: `${sourceData.fileName} 传输完成,耗时：${take} 秒`
      });
      ls()
      return
    }

    //计算预计剩余时间
    let speed = data.progress / take
    let left = (sourceData.fileSize - data.progress) / speed

    let speedStr = computedFileSize(speed) + '/s'

    notification.open({
      key: key,
      duration: 0,
      message: '文件传输',
      description: uploadFileProcess(sourceData, data, speedStr, take, left),
      btn: () =>
          h(
              Button,
              {
                type: 'primary',
                size: 'small',
                onClick: function () {
                  //按钮loading状态
                  sftpApi.cancelUploadTask({taskId: data.taskId})
                },
              },
              {default: () => '取消'},
          ),
    });

  }

  sftp.onmessage = _.throttle(handleProcessMessage, 1000, {
    leading: true,
    trailing: true,
    maxWait: 1000,
  })

  try {
    await sftpApi.serverUploadServer({
      sourceId: sourceData.sessionId,
      sourcePath: sourceData.currentPath,
      targetId: sessionId,
      targetPath: currentPath,
      fileName: sourceData.fileName,
      clientSessionId: authStore.session,
      sourceServerName: sourceData.serverName,
      targetServerName: props.serverName
    })


  } catch (e) {
    console.error(e)
    message.error(e.message)
  }
}


const onStart = (e, sessionId, currentPath, file, serverName) => {
  let dragData = {
    sessionId,
    currentPath,
    fileName: file.name,
    fileSize: file.attributes.size,
    serverName
  }
  localStorage.setItem('dragData', JSON.stringify(dragData))
}

const openCode = async (file) => {
  window.open(`/codeEditor?rootPath=${file ? (currentPath.value + '/' + file.name) : currentPath.value}&sessionId=${sessionId.value}&serverName=${props.serverName}&serverPath=${props.path}&serverId=${props.serverId}`)
}

</script>

<template>
  <div ref="sftpRootEl" class="sftp-root" @dragover="ondragover" @drop="drop($event,sessionId,currentPath)">
    <a-spin v-model:spinning="spinning" :tip="spinTip">
      <a-card :bordered="false" :hoverable="false">
        <template v-slot:title>
          <div class="head">
            <a-input ref="editInput" v-if="editPathVisible" v-model:value="currentPathEdit" @blur="handleChangePath"
                     @keyup.enter="handleChangePath"></a-input>
            <div v-else style="display: flex;align-items: center;padding-right: 48px" @click="startEditPath">
              <a-breadcrumb>
                <a-breadcrumb-item v-for="path in currentPathArray" :key="path.path" @click.stop="changeDir(path.path)">
                  <component :is="path.path===currentPath?'span':'a'" v-if="path.name!=='/'">{{ path.name }}</component>
                  <component :is="path.path===currentPath?'span':'a'" v-else>
                    <home-outlined/>
                  </component>
                </a-breadcrumb-item>
              </a-breadcrumb>
            </div>


            <div>
              <a-input-search class="ml10" style="width: 200px" @input="search"
                              v-model:value="searchValue"></a-input-search>
              <template v-if="buttonType === 'big'">
                <a-button class="ml10" @click="ls">刷新</a-button>

                <a-button class="ml10" @click="handleMkdir">新建目录</a-button>
                <a-button class="ml10" @click="handleUpload">上传</a-button>
                <a-select class="ml10" v-model:value="sortTypeValue" @change="handleChangeSort">
                  <a-select-option value="normal">默认排序</a-select-option>
                  <a-select-option value="date">按时间排序</a-select-option>
                  <a-select-option value="type">按类型排序</a-select-option>
                  <a-select-option value="size">按大小排序</a-select-option>
                </a-select>
                <a-button class="ml10" @click="openCode(null)">编辑器</a-button>
              </template>
              <template v-else-if="buttonType === 'normal'">
                <a-button class="ml10" @click="ls">
                  <reload-outlined/>
                </a-button>
                <a-button class="ml10" @click="handleMkdir">
                  <folder-add-outlined/>
                </a-button>
                <a-button class="ml10" @click="handleUpload">
                  <upload-outlined/>
                </a-button>
                <a-button class="ml10" @click="openCode(null)">
                  <form-outlined/>
                </a-button>
              </template>
              <template v-else>
                <a-dropdown>
                  <a class="ant-dropdown-link" style="font-size: 14px;" @click.prevent>
                    更多
                    <DownOutlined/>
                  </a>
                  <template #overlay>
                    <a-menu>
                      <a-menu-item key="0">
                        <a @click="ls">
                          <reload-outlined/>
                        </a>
                      </a-menu-item>
                      <a-menu-item key="1">
                        <a @click="handleMkdir">
                          <folder-add-outlined/>
                        </a>
                      </a-menu-item>
                      <a-menu-item key="2">
                        <a @click="handleUpload">
                          <upload-outlined/>
                        </a>
                      </a-menu-item>
                      <a-menu-item key="3">
                        <a @click="openCode(null)">
                          <form-outlined/>
                        </a>
                      </a-menu-item>
                    </a-menu>
                  </template>
                </a-dropdown>
              </template>

              <a-select class="ml10" v-model:value="sortTypeValue" @change="handleChangeSort">
                <a-select-option value="normal">
                  <unordered-list-outlined/>
                </a-select-option>
                <a-select-option value="date">
                  <svg t="1745219112489" class="icon" viewBox="0 0 1024 1024" version="1.1"
                       xmlns="http://www.w3.org/2000/svg" p-id="11970" width="14" height="14">
                    <path
                        d="M806.4 209.92H56.32c-20.992 0-38.4-17.408-38.4-38.4S35.328 133.12 56.32 133.12h750.08c20.992 0 38.4 17.408 38.4 38.4s-17.408 38.4-38.4 38.4zM391.68 532.48H56.32c-20.992 0-38.4-17.408-38.4-38.4S35.328 455.68 56.32 455.68h335.36c20.992 0 38.4 17.408 38.4 38.4s-17.408 38.4-38.4 38.4zM391.68 849.92l-335.36-1.024c-20.992 0-38.4-17.408-38.4-38.4s17.408-38.4 38.4-38.4l335.36 1.024c20.992 0 38.4 17.408 38.4 38.4 0 21.504-17.408 38.4-38.4 38.4z"
                        fill="#B2B2B2" p-id="11971"></path>
                    <path
                        d="M752.64 409.6c-141.312 0-256 114.688-256 256s114.688 256 256 256 256-114.688 256-256-114.688-256-256-256z m137.216 377.856c-7.68 9.216-18.432 13.824-29.696 13.824-8.704 0-17.408-3.072-24.576-8.704l-121.344-100.352V496.64c0-20.992 17.408-38.4 38.4-38.4s38.4 17.408 38.4 38.4v159.232l93.696 77.312c16.384 13.824 18.432 37.888 5.12 54.272z"
                        fill="#B2B2B2" p-id="11972"></path>
                  </svg>
                </a-select-option>
                <a-select-option value="type">
                  <svg t="1745219068738" class="icon" viewBox="0 0 1024 1024" version="1.1"
                       xmlns="http://www.w3.org/2000/svg" p-id="10970" width="14" height="14">
                    <path
                        d="M370.368 562.368v54.592h-114.56v278.656h-62.208V616.96h-106.88v-54.592h283.648zM627.072 819.2a32 32 0 0 1 32 32v10.88a32 32 0 0 1-32 32H510.272a32 32 0 0 1-32-32v-10.88a32 32 0 0 1 32-32h116.8z m60.288-217.216a32 32 0 0 1 32 32v10.88a32 32 0 0 1-32 32H510.272a32 32 0 0 1-32-32v-10.88a32 32 0 0 1 32-32h177.088zM250.88 140.48l-15.872 57.92h102.656l14.912-57.92h52.928l-13.952 57.92h56.768v43.584h-63.744l-26.88 109.888h76.672v46.08H351.68l-15.936 68.288H282.88l15.936-68.224H192.192l-17.92 68.224h-54.784l17.92-68.224h-50.624v-46.08h57.6l29.888-109.952H103.68V198.4h76.544l15.936-57.92h54.784z m556.992 244.416a32 32 0 0 1 32 32v10.88a32 32 0 0 1-32 32h-297.6a32 32 0 0 1-32-32v-10.88a32 32 0 0 1 32-32h297.6zM332.672 241.92H230.016l-30.848 109.888h105.6l27.904-109.888z m595.712-74.24a32 32 0 0 1 32 32v10.88a32 32 0 0 1-32 32H510.272a32 32 0 0 1-32-32v-10.88a32 32 0 0 1 32-32h418.112z"
                        fill="#647185" p-id="10971"></path>
                  </svg>
                </a-select-option>
                <a-select-option value="size">
                  <svg t="1745219034945" class="icon" viewBox="0 0 1024 1024" version="1.1"
                       xmlns="http://www.w3.org/2000/svg" p-id="9968" width="14" height="14">
                    <path
                        d="M597.333333 149.333333H128v85.333334h469.333333z m-469.333333 298.666667h384v-85.333333H128z m0 213.333333h298.666667v-85.333333H128z m0 213.333334h213.333333v-85.333334H128zM910.506667 682.666667L810.666667 781.226667V85.333333h-85.333334v693.76L626.773333 682.666667 554.666667 752.64l213.333333 207.36 213.333333-207.36z"
                        p-id="9969"></path>
                  </svg>
                </a-select-option>
              </a-select>


            </div>

          </div>
        </template>
        <div ref="parent" style="overflow: hidden;height: 100%">
          <a-card-grid draggable="true" @dragstart="onStart($event,sessionId,currentPath,file,serverName)"
                       :style="{width: width}"
                       :bordered="false"
                       :hoverable="false" v-for="(file,index) in currentPageData" :key="file.name"
                       @click="handleClickFile(file)" :title="file.name">
            <a-dropdown :trigger="['contextmenu']">
              <div>
                <div>
                  <img class="icon" :preview="false" :src="fileIcon" draggable="false"
                       v-if="file.attributes.type==='DIRECTORY'" alt="icon"/>
                  <svg v-else-if="file.attributes.type==='SYMLINK'" class="icon"
                       style="width:40%;height:40%;vertical-align: middle;fill: currentColor;overflow: hidden;"
                       viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24103">
                    <path
                        d="M625.792 302.912V64L1024 482.112l-398.208 418.176V655.36C341.312 655.36 142.208 750.912 0 960c56.896-298.688 227.584-597.312 625.792-657.088z"
                        fill="#262626" p-id="24104"></path>
                  </svg>
                  <img draggable="false" class="icon" :preview="false" :src="dirIcon" v-else alt="icon"/>
                </div>
                <div>
                  <a-input ref="renameInputs" @blur="confirmRename" @pressEnter="confirmRename"
                           v-if="currentRenameFileIndex===index" v-model:value="newFileName"
                           style="text-align: center"/>
                  <p v-else class="fileName">
                    {{ file.name }}
                  </p>
                </div>

                <div class="size">
                  {{ computedFileSize(file.attributes.size) }}
                </div>
                <div class="time">
                  {{ $f.datetime(file.attributes.mtime * 1000) }}
                </div>

              </div>
              <template #overlay>
                <a-menu>
                  <template v-if="!file.directory">
                    <a-menu-item key="4" @click="handleDownload(file)">
                      <cloud-download-outlined/>
                      下载
                    </a-menu-item>
                    <a-menu-item key="4" @click="handleClickFile(file)">
                      <info-circle-outlined/>
                      详细信息
                    </a-menu-item>
                  </template>
                  <a-menu-item v-else key="2" @click="openCode(file)">
                    <FolderOpenOutlined/>
                    打开编辑器
                  </a-menu-item>

                  <a-menu-item key="3" @click="handleRename(file,index)">
                    <edit-outlined/>
                    重命名
                  </a-menu-item>

                  <a-menu-item key="1" @click="handleDel(file)">
                    <DeleteOutlined/>
                    删除
                  </a-menu-item>
                </a-menu>
              </template>
            </a-dropdown>
          </a-card-grid>
        </div>
        <div v-if="searchFiles.length>50"
             style="margin-top: 16px;display: flex;flex-direction: row;justify-content: center">
          <a-pagination
              v-model:current="page"
              v-model:page-size="size"
              :total="searchFiles.length"
          />
        </div>
      </a-card>
    </a-spin>

    <a-modal v-model:visible="fileViewVisible" :title="fileView.name" ok-text="下载" cancel-text="关闭"
             @ok="handleDownload(fileView)"
             width="60%">
      <a-form>
        <a-form-item title="详细信息">
          <a-descriptions bordered>
            <a-descriptions-item label="名称" :span="2">{{ fileView.name }}</a-descriptions-item>
            <a-descriptions-item label="大小">{{ computedFileSize(fileView.attributes.size) }}</a-descriptions-item>
            <a-descriptions-item label="权限">{{
                permissionsToRwx(fileView.attributes.permissions)
              }}
            </a-descriptions-item>
            <a-descriptions-item label="用户">{{ fileView.attributes.uid }}</a-descriptions-item>
            <a-descriptions-item label="组">{{ fileView.attributes.gid }}</a-descriptions-item>
            <a-descriptions-item label="创建时间">{{
                $f.datetime(fileView.attributes.atime * 1000)
              }}
            </a-descriptions-item>
            <a-descriptions-item label="修改时间">{{
                $f.datetime(fileView.attributes.mtime * 1000)
              }}
            </a-descriptions-item>
          </a-descriptions>
        </a-form-item>
      </a-form>
    </a-modal>
    <a-modal v-model:visible="mkdirVisible" title="新建目录" @ok="mkdir" ok-text="创建" cancel-text="关闭">
      <a-input v-model:value="mkdirName" placeholder="请输入目录名"/>
    </a-modal>
  </div>
</template>

<style scoped lang="less">
.sftp-root {
  min-height: 100%;

  .head {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center
  }

  //min-height: @height;

  :deep(.ant-card-body) {
    margin-top: 24px;
    //height: calc(@height - 180px);
    padding: 12px;

  }


  :deep(.ant-card-grid) {
    box-shadow: none;

    width: calc(100% / 6);
    height: 150px;
    text-align: center;
    padding: 8px;
    font-size: 16px;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    justify-content: center;

    .icon {
      width: 50%;
      height: 50%;
    }

    &:hover {
      transform: scale(1.1);
    }

  }

  .fileName {
    width: 100%;
    text-align: center;
    font-size: 14px;
    line-height: 1.5;
    color: var(--context_primary);
    max-width: 100%;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
    overflow-wrap: break-word;
    margin-bottom: 2px;
    transition: color .3s ease;
    white-space: normal;
  }


  .time {
    color: rgba(0, 0, 0, .45);
    font-size: 12px;
    text-align: center;
  }

  .size {
    color: rgba(0, 0, 0, .45);
    font-size: 12px;
    text-align: center;
  }


}

.ml10 {
  margin-left: 10px;
}

.ml5 {
  margin-left: 5px;
}


</style>
